之前說明過Controller如何傳值給View,今天要來探討怎麼從View將資料傳回至Controller, 在MVC 裡有個Model Binding的功能,中文稱為模型綁定,就是在處裡如何從View傳值給Controller這件事,DefaultModelBinder可以將從url的參數值對應到Action的參數中,或是將值轉換成物件型態。
過去我自己寫php時,透過GET或POST傳遞的資料,都是要透過$_GET["id"]或是$_POST["id"]的方式取值,沒有什麼物件的概念,但在ASP.NET MVC中,幾乎都是透過Model Binding來處理參數,這樣做有一個好處就是資料就是強型別,而且就有自動提示可以用了!
接續昨天的範例,來觀察Model Binding是如何運作的
瀏覽User/並在清單頁點選一筆資料觀看詳細資訊,在網址上面可以看到User/Details/1
網址最後面的1其實就是透過get傳遞的id值,比較好理解的寫法應該是這樣User/Details?id=1,但為什麼url的顯示卻面成User/Details/1了呢,有認真看我文章的人應該知道為什麼吧(自言自語中),之前在講透過url存取Controller的時候有稍微提過路由設定,網址的結構都是透過路由去定義的,來複習一下App_Start資料夾下的RouteConfig,cs,下圖黃框的地方就是url的定義囉
在講回來Model Binding,在UserContorller的Details Action內設定一個中斷點,可以直接在程式碼最前面,也就是下圖右邊紅色圈圈的位置,按一下就可以產生或取消中斷點,那麼在專案偵錯的狀態下,透過滑鼠移到變數上面就能看到目前的執行狀態,例如我們可以看到在Details宣告的id已經被綁定了,有沒有很神奇,就不用像php那樣還要自己打$_GET["id"]的方式去取值
除了透過參數綁定的方式,也有提供透過FormCollection來綁定,使用上類似PHP取值的方式,就是透過["KeyName"]來操作,例如下圖,我們可以得到此表單所有的KeyName清單,如果想要知道值的內容,可以使用即時運算視窗,使用?再輸入變數名稱就可以印出資訊了
除了可以透過get的方式將url參數與Action參數綁定之外,還可以直接傳遞一個物件去綁定,聽起來很抽象吧!
表單透過Post傳遞值到Create Action內,並且定義可以傳遞一個User類別的參數,一樣透過中斷點觀察,在表單送出時,Action就已經完成綁定了,下圖右邊黃框就是綁定到的值,接著就可以能以物件的方式去取值,就不用自己寫一堆像PHP中的$_POST["ID"]、$_POST["Name"]的程式碼了
另位搭配ModelState.IsValid可以檢查表單中的資料是否都有符合驗證規則,若沒有的話會停留在原畫面,並顯示錯誤訊息
再來提一下上圖畫面中第三行的[HttpPost]是一個動作方法選擇器,意思就是說這個Action只會在表單是透過Post的方式下執行,或沒有宣告[HttpPost]那預設的Action動作就是使用[[HttpGet]
在UserContorller下有兩個同名的Create Action,若沒有宣告[HttpPost]的話,在專案執行時會有錯誤產生,因為Contorller沒辦法判斷到底要使用哪個Action。
第一個Create Action是第一次進入新增時的畫面,預設使用[[HttpGet]
第二個Create Action有宣告[HttpPost],是當表單送出時才會執行
透過物件綁定時,要特別注意物件下的屬性一定要宣告到{get;set;},不然是無法綁定的喔!
有沒有很好奇,MVC是如何完成這麼神奇的事情,剛開始學習的時候真的覺得超酷的,可以減少很多coding的時間,不過事情總是沒有憨人想的這麼簡單,有天遇到了一個需要在頁面中透過JS動態增加多個Email的需求,此時就是考驗你Model Binding觀念的時候了,當下我真的完全不知道要怎麼辦,好險強者同事點了我一下,我才了解到Model Binding的運作
來觀察一下User/Create新增使用者的HTML原始碼,可以觀察到使用者的輸入欄位input的name等於Name,這個"Name"就是模型綁定的重要依據,Model Binding會透過輸入欄位的name="Name"去綁定物件中的屬性名稱,例如User類別裡有一個Name屬性,那就能完成綁定
將HTML畫面準備好
將動態產生Email輸入欄位的JS寫好,特別注意input中name屬性的寫法
為什麼name屬性值要這樣寫當然是因為要根據Model的設計,我們可以觀察到User類別下有一個Email屬性而且是ICollection<Email>型態,再繼續觀察Email類別中有一個Value屬性,所以如果想要綁定到Email,input的name值無疑就是要寫成Email.Value,但是寫成這樣還不夠,因為Email在User類別下被宣告為集合,代表可以有多筆資料,所以必須再將index的值包含在name的資訊裡面,所以要寫成這樣Email[0].Value,代表第一筆資料
使用中斷點查看綁定狀態,將滑鼠移到user變數上面,就可以看到5筆為Email類型的資料已經被綁定
綁定這塊對初學者而言真的有點困難,起初在觀念上也是適應了很久才開始會解決問題,偏偏Model Binding還蠻重要的,所以一定要好好了解input的 name屬性與Model Binding之間的關係,而textArea與select tag也是一樣的邏輯。
下載今日專案: https://github.com/juben-wang/MvcApplication26